/*
 * Middleware
 * Copyright (C) 2010/11
 * Riccardo Pulvirenti, Giuseppe Ravidà, Andrea Tino
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANDABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see http://www.gnu.org/licenses
 */

/*!
 * File: workflow_test.hpp
 */

#ifndef _WORKFLOW_TEST_HPP_
#define _WORKFLOW_TEST_HPP_


#include <iostream>
#include <typeinfo>

#include "workflow.hpp"
#include "task_descriptor.hpp"
#include <boost/bind.hpp>
#include <boost/asio.hpp>
#include <boost/thread.hpp>
#include <boost/archive/binary_iarchive.hpp>
#include <boost/archive/binary_oarchive.hpp>

//-----------------------------------------------------------------------------
#define MODE_SERVER
//-----------------------------------------------------------------------------

using namespace std;
using boost::asio::ip::tcp;

namespace middleware {
//-----------------------------------------------------------------------------
// Definitions
/*!
 * Test function for Workflow.
 */
void WorkflowTest();
void WorkflowSerializationTest();
void WorkflowTCPSerializationTest();
//-----------------------------------------------------------------------------
// Implementations
void WorkflowTest() {
	TaskDescriptor task1;
	TaskDescriptor task2;
	TaskDescriptor task3;
	TaskDescriptor task4;
	task1.set_id(10);
	task2.set_id(20);
	/*!
	 * Testing copy constructor
	 */
	Workflow wf0;
	wf0.push_back(task1);
	wf0.push_back(task2);
	wf0.push_back(task3);
	wf0.push_back(task4);
	std::cout << "size0: " << wf0.size() << std::endl;
	Workflow::TaskCollectionIterator it0;
	for (it0 = wf0.begin(); it0 < wf0.end(); it0++) {
		std::cout << "task " << it0->get_id() << std::endl;
	}
	Workflow wf00; wf00 = wf0;
	std::cout << "size00: " << wf00.size() << std::endl;
	/*!
	 * Testing the default constructor.
	 */
	Workflow wf1;
	Workflow wfCpy;
	/*!
	 * Testing the copy constructor.
	 */
	Workflow wf2(wf1);
	Workflow wf3;
	wfCpy = wf3;
	/*!
	 * Testing set_id.
	 */
	wf1.set_id(10);
	wf2.set_id(20);
	wf3.set_id(30);
	wfCpy.set_id(40);
	/*!
	 * Testing get_id.
	 */	
	cout << "wf1 ID: " << wf1.get_id() << std::endl;
	cout << "wf2 ID: " << wf2.get_id() << std::endl;
	cout << "wf3 ID: " << wf3.get_id() << std::endl;
	cout << "wf4 ID: " << wfCpy.get_id() << std::endl;
	wf1.set_id(11);
	wf3.set_id(33);
	cout << "wf1 ID: " << wf1.get_id() << std::endl;
	cout << "wf2 ID: " << wf2.get_id() << std::endl;
	cout << "wf3 ID: " << wf3.get_id() << std::endl;
	cout << "wf4 ID: " << wfCpy.get_id() << std::endl;
	/*!
	 * Testing push_back.
	 */
	wf1.push_back(task1);
	wf1.push_back(task2);
	/*!
	 * Testing size
	 */
	cout << "Size: " << wf1.size() << endl;
	/*!
	 * Testing iterators.
	 */	
	cout << "Testing Iterators..." << std::endl;
	Workflow::TaskCollectionIterator it;	
	for(it=wf1.begin(); it<wf1.end(); it++) {
		cout << "-" << it->get_id() << std::endl;
	}
}

void WorkflowSerializationTest() {
	stringstream ss (stringstream::in | stringstream::out);

	TaskDescriptor t1;
	TaskDescriptor t2;

	t1.set_id(10);
	t2.set_id(20);

	std::vector<TaskDescriptor> tasks;
	tasks.push_back(t1);
	tasks.push_back(t2);

	Workflow wf;

	wf.assign((Workflow::TaskCollectionIterator)(tasks.begin()),(Workflow::TaskCollectionIterator)(tasks.end()));

//	wf.push_back(t1);
//	wf.push_back(t2);

	{
		boost::archive::text_oarchive oa(ss);
		oa << wf;
	}

	Workflow newWF;
	{
		// create and open an archive for input
		boost::archive::text_iarchive ia(ss);
		// read class state from archive
		ia >> newWF;
		// archive and stream closed when destructors are called
	}

	cout << "Serialization test.If this print " << wf[1].get_id() << ",it works:"
			<< newWF[1].get_id() << std::endl;
}

void WorkflowTCPSerializationTest() {

	#ifdef MODE_SERVER
		// Server
		try {
			Workflow newWf;

			boost::asio::io_service io_s; /* Create an io_service */
			boost::asio::ip::tcp::acceptor acc(io_s,
					boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(),
							1950)); /* Configure acceptor */
			tcp::iostream stream; /* Define a stream */
			acc.accept(*stream.rdbuf()); /* Accept and get the rdbuf in the request and let my buffer point to it */

			for (;;) {
				cout << "-1-" << endl;
				boost::archive::binary_iarchive ia(stream);
				cout << "-2-" << endl;
				ia >> newWf;

				std::cout << "Client: " << newWf[1].get_id() << std::endl;
				std::cout << "Comando: " << newWf[1].get_command() << endl;
				stream << "Grazie!" << std::endl;
			}

		} catch(std::exception& e) {
			std::cout << e.what() << std::endl;
		}
	#else
		// Client
		try {

			TaskDescriptor task1;
			TaskDescriptor task2;
			Workflow wf;

			task1.set_id(10);
			task2.set_id(20);
			task1.set_command("comando 1");
			task2.set_command("comando 2");
			wf.push_back(task1);
			wf.push_back(task2);

			tcp::iostream stream("127.0.0.1", "1950"); // Creates stream ALREADY BINDED TO AN INET ADDR
			std::string command; /* I should receive and send commands */
			if (!stream) {
				throw std::runtime_error("Could not connect!");
			} else {
				for (;;) {
					boost::archive::binary_oarchive oa(stream);
					oa << wf;

					cout << wf[1].get_id() << endl;

					if (std::getline(stream, command)) std::cout << "Server: " << command << std::endl; /* Line approach for receiving */
					boost::this_thread::sleep(boost::posix_time::milliseconds(3000));
				}
			}

		} catch(std::exception& e) {
			std::cout << e.what() << std::endl;
		}
#endif
}

} /* middleware */

#endif
